/*
 *    Copyright 2022 The DSMS Authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.dsms.modules.alert.service.impl;


import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dsms.common.constant.*;
import com.dsms.common.exception.DsmsEngineException;
import com.dsms.modules.alert.AlertNotifyDispatcher;
import com.dsms.modules.alert.entity.AlertApprise;
import com.dsms.modules.alert.entity.AlertContact;
import com.dsms.modules.alert.entity.AlertMessage;
import com.dsms.modules.alert.mapper.AlertAppriseMapper;
import com.dsms.modules.alert.service.IAlertAppriseService;
import com.dsms.modules.alert.service.IAlertContactService;
import com.dsms.modules.alert.service.IAlertMessageService;
import com.dsms.modules.mail.model.MailMessage;
import com.dsms.modules.mail.model.convert.MailMessageConvert;
import com.dsms.modules.mail.service.IMailSendService;
import com.dsms.modules.sms.SmsClientFactory;
import com.dsms.modules.sms.model.SmsSendMessage;
import com.dsms.modules.sms.model.convert.SmsMessageConvert;
import com.dsms.modules.sms.service.ISmsSendService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.Objects;

import static com.dsms.common.constant.ResultCode.*;


@Service
public class AlertAppriseServiceImpl extends ServiceImpl<AlertAppriseMapper, AlertApprise> implements IAlertAppriseService {

    public static final String ALERT_TEST_CONTENT = "This is the DSMS email test email.";

    public static final String ALERT_TEST_TITLE = "Dsms 告警通知测试";

    @Autowired
    private IAlertContactService alertContactService;

    @Autowired
    @Lazy
    private IMailSendService mailService;

    @Autowired
    @Lazy
    private ISmsSendService smsSendService;

    @Autowired
    private SmsClientFactory smsClientFactory;

    @Autowired
    @Lazy
    private AlertNotifyDispatcher alertNotifyDispatcher;

    @Autowired
    private IAlertMessageService alertMessageService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveOrUpdateAlertApprise(AlertApprise alertApprise) {
        if (validateAlertApprise(alertApprise)) {
            try {
                boolean flag = saveOrUpdate(alertApprise);
                if (flag) {
                    AlertApprise updateAlertApprise = this.getById(alertApprise.getId());
                    if (AlertAppriseTypeEnum.SMS.getCode().equals(updateAlertApprise.getType())) {
                        smsClientFactory.createOrUpdateSmsClient(updateAlertApprise);
                    } else if (AlertAppriseTypeEnum.EMAIL.getCode().equals(updateAlertApprise.getType())) {
                        mailService.updateMailAccount(updateAlertApprise);
                    }
                }
                return true;
            } catch (Exception e) {
                throw DsmsEngineException.exceptionWithThrowable(e, ALERT_APPRISE_PARAM_ERROR);
            }
        }
        return false;
    }

    @Override
    public boolean sendTestEmail() {
        //1.get email smtp config
        LambdaQueryWrapper<AlertApprise> alertAppriseQuery = Wrappers.lambdaQuery(AlertApprise.class)
                .eq(AlertApprise::getType, AlertAppriseTypeEnum.EMAIL.getCode());
        List<AlertApprise> alertApprises = this.list(alertAppriseQuery);
        if (ObjectUtils.isEmpty(alertApprises)) {
            throw new DsmsEngineException(ALERT_APPRISE_PARAM_ERROR);
        }
        validateSmtpParam(alertApprises.get(0));

        //2.get alert apprise target emails
        LambdaQueryWrapper<AlertContact> alertContactQuery = Wrappers.lambdaQuery(AlertContact.class)
                .select(AlertContact::getContactAccount)
                .eq(AlertContact::getContactType, AlertAppriseTypeEnum.EMAIL.getCode());
        List<String> emailList = alertContactService.listObjs(alertContactQuery, Object::toString);

        //3.send email
        try {
            MailMessage mailMessage = MailMessageConvert.convert(alertApprises.get(0), ALERT_TEST_TITLE, ALERT_TEST_CONTENT, emailList);
            mailService.sendMail(mailMessage);
        } catch (Exception e) {
            throw DsmsEngineException.exceptionWithThrowable(e, ResultCode.ALERT_APPRISE_EMAIL_TEST_ERROR);
        }
        return true;
    }


    @Override
    public boolean sendTestSms() {
        //1.get email smtp config
        LambdaQueryWrapper<AlertApprise> alertAppriseQuery = Wrappers.lambdaQuery(AlertApprise.class)
                .eq(AlertApprise::getType, AlertAppriseTypeEnum.SMS.getCode());
        List<AlertApprise> alertApprises = this.list(alertAppriseQuery);
        if (ObjectUtils.isEmpty(alertApprises)) {
            throw new DsmsEngineException(ALERT_APPRISE_PARAM_ERROR);
        }
        validateSmsParam(alertApprises.get(0));

        //2.get alert apprise test target mobile
        LambdaQueryWrapper<AlertContact> alertContactQuery = Wrappers.lambdaQuery(AlertContact.class)
                .select(AlertContact::getContactAccount)
                .eq(AlertContact::getContactType, AlertAppriseTypeEnum.SMS.getCode());
        List<String> mobiles = alertContactService.listObjs(alertContactQuery, Object::toString);

        //3.do send sms
        try {
            SmsSendMessage smsSendMessage = SmsMessageConvert.convert(alertApprises.get(0), mobiles);
            smsSendService.doSendSms(smsSendMessage);
        } catch (Exception e) {
            throw DsmsEngineException.exceptionWithThrowable(e, ResultCode.ALERT_APPRISE_SMS_TEST_ERROR);
        }
        return true;
    }

    @Override
    public List<AlertApprise> listNotifyAlertApprise() {
        LambdaQueryWrapper<AlertApprise> enableAlertAppriseQuery = Wrappers.lambdaQuery(AlertApprise.class)
                .eq(AlertApprise::getStatus, CommonEnum.ENABLE.getCode());
        List<AlertApprise> enableAlertApprises = this.list(enableAlertAppriseQuery);
        for (AlertApprise apprise : enableAlertApprises) {
            List<AlertContact> alertContacts = alertContactService.list(Wrappers.lambdaQuery(AlertContact.class)
                    .eq(AlertContact::getContactType, apprise.getType()));
            apprise.setContacts(alertContacts);
        }

        return enableAlertApprises;
    }

    @Override
    public void notifyContact(List<AlertMessage> alertMessages) {
        List<AlertApprise> enableAlertApprises = listNotifyAlertApprise();
        for (AlertMessage alertMessage : alertMessages) {
            for (AlertApprise apprise : enableAlertApprises) {
                boolean shouldSendSms = Objects.equals(apprise.getType(), AlertAppriseTypeEnum.SMS.getCode()) && Objects.equals(alertMessage.getSmsStatus(), AlertMessageAppriseStatusEnum.UP_BUT_NOT_APPRISED.getCode());
                boolean shouldSendEmail = Objects.equals(apprise.getType(), AlertAppriseTypeEnum.EMAIL.getCode()) && Objects.equals(alertMessage.getEmailStatus(), AlertMessageAppriseStatusEnum.UP_BUT_NOT_APPRISED.getCode());
                if (shouldSendSms || shouldSendEmail) {
                    alertNotifyDispatcher.sendNotice(apprise, alertMessage);
                }
            }
        }
    }

    @Override
    public void notifyContact(String message,AlertRuleModuleEnum alertRuleModuleEnum,AlertRuleLevelEnum alertRuleLevelEnum) {
        AlertMessage alertMessage = alertMessageService.convertCustomMessage(message, alertRuleModuleEnum, alertRuleLevelEnum);
        alertMessageService.save(alertMessage);
        List<AlertApprise> enableAlertApprises = listNotifyAlertApprise();
        for (AlertApprise apprise : enableAlertApprises) {
            alertNotifyDispatcher.sendNotice(apprise, alertMessage);
        }
    }

    boolean validateAlertApprise(AlertApprise alertApprise) {
        return validateAlertAppriseField(alertApprise);
    }

    private boolean validateAlertAppriseField(AlertApprise alertApprise) {
        if (AlertAppriseTypeEnum.EMAIL.getCode().equals(alertApprise.getType())) {
            validateSmtpParam(alertApprise);
        } else if (AlertAppriseTypeEnum.SMS.getCode().equals(alertApprise.getType())) {
            validateSmsParam(alertApprise);
        } else {
            throw new DsmsEngineException(ResultCode.ALERT_APPRISE_TYPE_ERROR);
        }

        return true;
    }

    private void validateSmtpParam(AlertApprise alertApprise) {
        if (CharSequenceUtil.isBlank(alertApprise.getSmtpHost())) {
            throw new DsmsEngineException(EMAIL_HOST_EMPTY);
        }
        if (ObjectUtils.isEmpty(alertApprise.getSmtpPort())) {
            throw new DsmsEngineException(EMAIL_PORT_EMPTY);
        }
        if (CharSequenceUtil.isBlank(alertApprise.getSmtpUsername())) {
            throw new DsmsEngineException(EMAIL_USERNAME_EMPTY);
        }
        if (CharSequenceUtil.isBlank(alertApprise.getSmtpPassword())) {
            throw new DsmsEngineException(EMAIL_PASSWORD_EMPTY);
        }
        if (CharSequenceUtil.isBlank(alertApprise.getEmailFrom())) {
            throw new DsmsEngineException(EMAIL_SENDER_EMPTY);
        }
    }

    private void validateSmsParam(AlertApprise alertApprise) {
        if (CharSequenceUtil.isBlank(alertApprise.getSmsProvider())) {
            throw new DsmsEngineException(SMS_PROVIDER_EMPTY);
        }
        if (CharSequenceUtil.isBlank(alertApprise.getSmsProviderHost())) {
            throw new DsmsEngineException(SMS_PROVIDER_HOST_EMPTY);
        }
        if (CharSequenceUtil.isBlank(alertApprise.getSmsAccessId())) {
            throw new DsmsEngineException(SMS_ACCESS_ID_EMPTY);
        }
        if (CharSequenceUtil.isBlank(alertApprise.getSmsAccessSecret())) {
            throw new DsmsEngineException(SMS_ACCESS_SECRET_EMPTY);
        }
        if (CharSequenceUtil.isBlank(alertApprise.getSmsTemplateCode())) {
            throw new DsmsEngineException(SMS_TEMPLATE_CODE_EMPTY);
        }
        if (CharSequenceUtil.isBlank(alertApprise.getSmsSignName())) {
            throw new DsmsEngineException(SMS_SIGN_EMPTY);
        }
        if (AlertSmsProviderEnum.TENCENT.getCode().equals(alertApprise.getSmsProvider())) {
            if (CharSequenceUtil.isBlank(alertApprise.getSmsRegion())) {
                throw new DsmsEngineException(SMS_REGION_EMPTY);
            }
            if (CharSequenceUtil.isBlank(alertApprise.getSmsAppId())) {
                throw new DsmsEngineException(SMS_APP_ID_EMPTY);
            }
        }
    }
}
